
#ifndef STOUT_STOUT_H
#define STOUT_STOUT_H

#include <map>
#include <vector>
#include <list>
#include <string>

#include <stdarg.h>
#include <iostream>
#include <set>


namespace stout
{

	typedef enum {
		START,
		ID,
		VAR,
		NUM,
		LCURL,
		RCURL,
		LPAREN,
		RPAREN,
		TRY,
		WHILE,
		EQUALS,
		PLUS,
		MINUS
	} lexeme;


	class chunk;

	class token
	{
	public:
		std::string Source;
		lexeme Lexeme;

		token (const std::string &_src);
		token () : Source ("START"), Lexeme (START)
		{
		}
	};

	class basesymbol : public token, public std::list <basesymbol *>
	{
		basesymbol *Parent;
		std::map <std::string, basesymbol *> Scope;

	public:
		basesymbol (token &_token, basesymbol *_parent) : token (_token), Parent (_parent), Scope (Parent->Scope)
		{
			std::cout << "new " << Source << std::endl;
		}

		basesymbol () : Parent (NULL)
		{
		}

		~basesymbol ()
		{
			for (std::list <basesymbol *>::iterator pos = begin (); pos != end (); ++pos)
			{
				std::cout << "delete " << Source << std::endl;
				delete *pos;
			}
		}

		virtual void close (chunk *_chunk) = 0;

		void define (std::string &_id)
		{
			Scope [_id] = this;
		}

		basesymbol *reference (std::string &_id)
		{
			std::map <std::string, basesymbol *>::iterator pos = Scope.find (_id);
			
			if (pos == Scope.end ())
			{
				throw std::string ("reference to undefined variable `") + _id + std::string ("'.");
			}
			else
			{
				return pos->second;
			}
		}
	};


	template <class T>
	class symbol : public basesymbol
	{
	public:
		symbol (token &_token, basesymbol *_parent) : basesymbol (_token, _parent)
		{
		}

		symbol ()
		{
		}

		virtual void close (chunk *_chunk)
		{
			new T (this, _chunk);
		}
	};


	class lexer : public std::list <token>
	{
		std::string SourceName;
		unsigned int Line;

	public:
		lexer (const std::string &_srcname) : SourceName (_srcname), Line (0)
		{
		}

		void next ()
		{
			if (empty ())
			{
				throw std::string ("unexpected end of input.");
			}

			pop_front ();
		}

		friend std::istream &operator >> (std::istream &_in, lexer &_lex);
	};


	typedef double number;
	typedef int integer;
	
	class object 
	{
	public:
		virtual operator std::string ()
		{
			throw std::string ("invalid coercion from `unknown' to `string'");
		}

		virtual operator integer ()
		{
			throw std::string ("invalid coercion from `unknown' to `integer'");
		}

		virtual operator number ()
		{
			throw std::string ("invalid coercion from `unknown' to `number'");
		}
		
		virtual operator object * ()
		{
			return this;
		}

		virtual operator bool ()
		{
			return false;
		}
		
		virtual std::string type ()
		{
			return "nil";
		}

		virtual std::string name ()
		{
			return "anonymous";
		}
	
	};

	class primitive : public object
	{
		union
		{
			std::string *String;
			number Number;
			integer Integer;
			object *Reference;
			bool Boolean;
		} Value;

		enum
		{
			STRING, NUMBER, INTEGER, REFERENCE, BOOLEAN, NIL
		} Type;

	public:
		primitive () : Type (NIL)
		{
		}

		object &operator = (std::string &_str)
		{
			Value.String = &_str;
			Type = STRING;

			return *this;
		}

		object &operator = (number _n)
		{
			Value.Number = _n;
			Type = NUMBER;

			return *this;
		}

		object &operator = (integer _i)
		{
			Value.Integer = _i;
			Type = INTEGER;

			return *this;
		}

		object &operator = (bool _b)
		{
			Value.Boolean = _b;
			Type = BOOLEAN;

			return *this;
		}

		object &operator = (object *_obj)
		{
			Value.Reference = _obj;
			Type = REFERENCE;

			return *this;
		}

		virtual operator std::string ()
		{
			switch (Type)
			{
			case STRING :
				return *Value.String;

			case NUMBER :
				{
					char buf [40];

					snprintf (buf, 40, "%f", Value.Number);

					return buf;
				}

			case INTEGER :
				{
					char buf [40];

					snprintf (buf, 40, "%i", Value.Integer);

					return buf;
				}

			case REFERENCE :
				return *Value.Reference;

			case BOOLEAN :
				if (Value.Boolean)
				{
					return "true";
				}
				else
				{
					return "false";
				}

			case NIL :
				throw std::string ("invalid coercion from `nil' to `string'; attempt to access nil object.");

			default:
				throw std::string ("internal error: bad primitive type in coersion to `string'.");
			};
		}

		virtual operator number ()
		{
			switch (Type)
			{
			case STRING :
				return atof (Value.String->c_str ());

			case NUMBER :
				return Value.Number;

			case INTEGER :
				return static_cast <number> (Value.Integer);

			case REFERENCE :
				return *Value.Reference;

			case BOOLEAN :
				throw std::string ("invalid coercion from `boolean' to `number'.");

			case NIL :
				throw std::string ("invalid coercion from `nil' to `number'; attempt to access nil object.");

			default:
				throw std::string ("internal error: bad primitive type in coersion to `number'.");
			};
		}

		virtual operator integer ()
		{
			switch (Type)
			{
			case STRING :
				return atoi (Value.String->c_str ());

			case NUMBER :
				throw std::string ("invalid coersion from `number' to `integer'; possible loss of information.");

			case INTEGER :
				return Value.Integer;

			case REFERENCE :
				return *Value.Reference;

			case BOOLEAN :
				return Value.Boolean;

			default:
				throw std::string ("internal error: bad primitive type in coersion to `integer'.");
			};
		}

		virtual operator object *()
		{
			if (Type == REFERENCE)
			{
				return Value.Reference;
			}
			else
			{
				//TODO: reference counting
				return this;
			}
		}
	};

			
	// property
	class named
	{
		std::string Name;

	public:
		named (std::string _name) : Name (_name)
		{
		}

		virtual std::string name ()
		{
			return Name;
		}
	};


	// The building blocks of programs...
	// Chunks have no environment, so should usually be a
	// descendent of a closure.
	class chunk
	{
	protected:
		chunk *Parent;
		std::list <chunk *> Children;

	public:
		// build a block from a node in an AST
		chunk (basesymbol *_sym, chunk *_parent) : Parent (_parent)
		{
			if (Parent != NULL)
			{
				Parent->Children.push_back (this);
			}

			for (basesymbol::iterator pos = _sym->begin (); pos != _sym->end (); ++pos)
			{
				(*pos)->close (this);
			}
		}

		~chunk ()
		{
			for (std::list <chunk *>::iterator pos = Children.begin (); pos != Children.end (); ++pos)
			{
				delete *pos;
			}
		}

	// Since chunks have no environment, they must ask their parents
	// about variables:
	
		virtual void set (const std::string &_k, primitive &_v)
		{
			if (Parent == NULL)
			{
				throw std::string ("assignment to undefined variable `") + _k + std::string ("'.");
			}

			Parent->set (_k, _v);
		}

		virtual primitive &get (const std::string &_k)
		{
			if (Parent == NULL)
			{
				throw std::string ("reference to undefined variable `") + _k + std::string ("'.");
			}

			return Parent->get (_k);
		}

		virtual void checkout (std::map <std::string, primitive> &_env)
		{
			if (Parent == NULL)
			{
				throw std::string ("internal error: attempt to checkout bad environment.");
			}

			Parent->checkout (_env);
		}

		virtual void commit (std::map <std::string, primitive> &_env)
		{
			if (Parent == NULL)
			{
				throw std::string ("internal error: attempt to commit to bad environment.");
			}

			Parent->commit (_env);
		}
	};


	// Closures implement blocks and functions and are loaded in parallel 
	// with each closure occupying its own thread.
	class closure : public chunk, protected object
	{
	private:
		pthread_mutex_t Mutex;
		pthread_t Thread;
				
	public:
		// These constitute the closure's "environment".
		// Primitives are used because higher-order objects
		// are accessed thru references.
		std::map <std::string, primitive> DownValues;	// inherited
		std::map <std::string, primitive> UpValues;		// sent upwards in one big transaction
		std::map <std::string, primitive> Locals;		// local variables

		// build a closure from a node in an AST
		closure (basesymbol *_sym, chunk *_parent) : chunk (_sym, _parent)
		{
			if (Parent != NULL)
			{
				Parent->checkout (DownValues);
			}
		}

		~closure ()
		{
			if (Parent != NULL)
			{
				Parent->commit (UpValues);
			}
		}

		virtual void set (const std::string &_k, primitive &_v)
		{
			std::cout << "assignment!!\n";

			std::map <std::string, primitive>::iterator pos;
			
			if ((pos = Locals.find (_k)) != Locals.end ())
			{
				pos->second = _v;
			}
			else if (DownValues.find (_k) != DownValues.end ())
			{
				UpValues [_k]  = _v;
			}
			else
			{
				pos->second = _v;
			}
		}

		virtual primitive &get (const std::string &_k)
		{
			std::map <std::string, primitive>::iterator pos;

			if ((pos = Locals.find (_k)) != Locals.end ())
			{
				return pos->second;
			}
			else if ((pos = UpValues.find (_k)) != UpValues.end ())
			{
				return pos->second;
			}
			else if (DownValues.find (_k) != DownValues.end ())
			{
				return UpValues [_k] = pos->second;
			}
			else
			{
				throw std::string ("reference to undefined variable `") + _k + std::string ("'.");
			}
		}

		virtual void checkout (std::map <std::string, primitive> &_env)
		{
			for (std::map <std::string, primitive>::iterator pos = Locals.begin (); pos != Locals.end (); ++pos)
			{
				_env.insert (*pos);
			}
		}

		virtual void commit (std::map <std::string, primitive> &_env)
		{
			for (std::map <std::string, primitive>::iterator pos = _env.begin (); pos != _env.end (); ++pos)
			{
				set (pos->first, pos->second);
			}
		}
		
		virtual std::string type ()
		{
			return "closure";
		}
	};


	class expression : public chunk, public primitive
	{
	public:
		expression (basesymbol *_sym, chunk *_parent) : chunk (_sym, _parent)
		{
			std::cout << "starting expression...\n";

			for (basesymbol::iterator pos = _sym->begin (); pos != _sym->end (); ++pos)
			{
				(*pos)->close (this);
			}
		}

		~expression ()
		{
			std::cout << "...ending expression.\n";
		}
	};

	
	class assignment : public expression
	{
		std::string ID;
	public:
		assignment (basesymbol *_sym, chunk *_parent) : expression (_sym, _parent), ID (_sym->Source)
		{
		}

		~assignment ()
		{
			set (ID, *this);
		}
	};


	class parser
	{
	protected:
		lexer Lexer;
		symbol <closure> Root;
		std::list <basesymbol *> Stack;

	public:
		parser (const std::string &_srcname) : Lexer (_srcname)
		{
		}

		~parser ()
		{
		}

		void define ()
		{
			Stack.front ()->define (Lexer.front ().Source);
		}

		void reference ()
		{
			try
			{
				Stack.front ()->reference (Lexer.front ().Source);
			}
			catch (std::string &_err)
			{
				throw std::string ("parser: ") + _err;
			}
		}

		template <class T>
		void attach ()
		{
			if (Stack.empty ())
			{
				Root.push_back (new symbol <T> (Lexer.front (), &Root));
			}
			else
			{
				Stack.back ()->push_back (new symbol <T> (Lexer.front (), Stack.back ()));
			}
		}

		template <class T>
		void push ()
		{
			if (Stack.empty ())
			{
				Stack.push_back (new symbol <T> (Lexer.front (), &Root));
				Root.push_back (Stack.back ());
			}
			else
			{
				basesymbol *p = Stack.back ();
				Stack.push_back (new symbol <T> (Lexer.front (), p));
				p->push_back (Stack.back ());
			}
		}

		void pop ()
		{
			if (Stack.empty ())
			{
				throw std::string ("unexpected end of scope.");
			}

			Stack.pop_back ();
		}

		void close (closure *_closure)
		{
			Root.close (_closure);
		}

		friend std::istream &operator >> (std::istream &_in, parser &_parse);

	private:
		void block ();		// '{' stmnts '}'
		bool stmnt ();		// lhand '=' rhand | 'if' expr block | 'if' expr block 'else' block | 'while' block
		void lhand ();		// id | id '(' tuple ')' | id '[' tuple ']'
		void expr ();
		void term ();
	};
	
};

#endif
